/*
 * EnableGate.java
 * 
 * last update: 16.01.2010 by Stefan Saru
 * 
 * author:	Alec(panovici@elcom.pub.ro)
 * 
 * Obs:
 */

package engine;

import java.util.*;
import java.io.*;
import middle.*;

/**
 * guess..
 */
class EnableGate extends StdGate implements Monitor, Executable{

	BitVector lastInput, lastOutput;
	byte lastStrength[];
	Expression input;
	LeftValue output;
	byte strength;

	/**
	 * The pre-calculated strengths for 0/1, L and H
	 */
	static byte strengths[];
	static BitVector values[] = {BitVector.b0(), BitVector.b1()};

	/**
	 * Used as an index into strengthTable
	 */
	int strengthIndex;

	static final byte [] strengthTable[] =
	{//bufif0, bufif1
		{0, 0, 1, 1,
			0, 0, 2, 2,
			0, 0, 0, 0,
			0, 0, 0, 0
		},
		//notif0, notif1
		{0, 0, 2, 2,
			0, 0, 1, 1,
			0, 0, 0, 0,
			0, 0, 0, 0
		},
	};

	EnableGate(NameSpace parent, Delay3 delay,
			LeftValue output, Expression input,
			ModuleInstanceDescription id, byte type,
			byte strength)
			throws ParseException
			{
		super(parent, delay, type, id);
		this.output = output;
		this.input = input;
		input.addMonitor(this);
		this.strength = strength;
		strengths = new byte[3];
		if(strength == StdGateDescription.bufif0 ||
				strength == StdGateDescription.bufif1)
			strengthIndex = 0;
		else strengthIndex = 1;
		lastStrength = new byte[1];
		/*
		 * We represent L as a 0 value with strength varying from
		 * the given strength to highz and
		 * and H as a 1 value with the strength varying from
		 * the given strength to highz.
		 * Note that although strength specifiers like
		 * (highz1, strong1) are illegal, this is exactly what
		 * a bufif.. will output when its strength spec. is 'strong'
		 * and the output state is 'H'.
		 */
		strengths[0] = strength;
		strengths[1] = Strength.getStrength(Strength.highz,
				Strength.getStrength0(strength),
				0);
		strengths[2] = Strength.getStrength(Strength.highz,
				Strength.getStrength1(strength),
				1);
		lastUpdateTime = -1;
		try {
			lastInput = input.evaluate().getBits();
		} catch (InterpretTimeException itex) {
			throw new ParseException (itex.toString());
		}
		lastStrength[0] = (byte)0xff;
		lastOutput = BitVector.bZ();
			}

	public void trigger()throws InterpretTimeException {

		BitVector curInput = input.evaluate().getBits();
		xConsole.debug("Enable: trigger: lastInput: " + lastInput +
				" curInput: " + curInput);
		if (curInput.equals(lastInput)) return;
		lastInput = curInput;

		BitVector curOutput = (BitVector) curInput.duplicate();
		curOutput.genericReduction1(truthTables[type]);
		byte curStrength =
			strengths[strengthTable[strengthIndex]
			                        [(curInput.getb(1) <<2) | curInput.getb(0)]];

		xConsole.debug("output: " +
				Strength.strengthToString(curStrength,
						curOutput.getb(0)));
		if (curOutput.equals(lastOutput) &&
				(curStrength == lastStrength[0]))
			return;
		int iDelay = Wire.computeTransitionDelayFor(lastOutput, curOutput, delay);
		lastOutput = curOutput;
		lastStrength[0] = curStrength;
		lastUpdateTime = Time.oClock() + iDelay;
		Time.addFinisher(iDelay, new GenericInstruction(this));
	}

	public void execute ()throws InterpretTimeException {
		if (lastUpdateTime < Time.oClock()) return; //not now
		xConsole.trace(this + ": " + output + " <<< " + lastOutput);
		((WireSelection)output).setStrengths(lastStrength, 0);
		output.assign(lastOutput);
	}

}



